package cn.sunshine.ymcc.service.impl;

import cn.sunshine.ymcc.domain.Course;
import cn.sunshine.ymcc.domain.CourseMarket;
import cn.sunshine.ymcc.domain.CourseOrder;
import cn.sunshine.ymcc.domain.CourseOrderItem;
import cn.sunshine.ymcc.dto.CoursePlaceOrderDto;
import cn.sunshine.ymcc.dto.Order2PayParamDto;
import cn.sunshine.ymcc.dto.PayAliNotifyDto;
import cn.sunshine.ymcc.exception.GlobleException;
import cn.sunshine.ymcc.fegin.CourseUserLeanFeign;
import cn.sunshine.ymcc.mapper.CourseOrderMapper;
import cn.sunshine.ymcc.result.JSONResult;
import cn.sunshine.ymcc.service.ICourseOrderItemService;
import cn.sunshine.ymcc.service.ICourseOrderService;
import cn.sunshine.ymcc.util.AssertUtil;
import cn.sunshine.ymcc.util.CodeGenerateUtils;
import cn.sunshine.ymcc.vo.CourseInfosTotalAmountVo;
import cn.sunshine.ymcc.vo.CourseInfosVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Mr.Jiang
 * @since 2022-08-19
 */
@Slf4j
@Service
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private CourseUserLeanFeign courseUserLeanFeign;
    @Autowired
    private ICourseOrderItemService courseOrderItemService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    public static final String TOKEN = "token:%s:%s";

    @Override
    public String placeOrder(CoursePlaceOrderDto coursePlaceOrderDto) {
        // 1.防重token校验
        Long loginId = 3L;
        // 2.生成防重token的CourseIds可能是一个页=也可能是多个，所有采用string类型
        String CourseIds = StringUtils.join(coursePlaceOrderDto.getCourseIds(), ",");
        String key = String.format(TOKEN, loginId, CourseIds);
        Object tokenValue = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(tokenValue, "防重token失效，请勿重复下单");
        boolean equals = coursePlaceOrderDto.getToken().equals(tokenValue);
        AssertUtil.isTrue(equals, "token不一致，你不要搞事");
        // 调用courseUserLeanFeign api --->courseMarket
        JSONResult courseInfosTotalAmount = courseUserLeanFeign.getCourseInfosTotalAmount(CourseIds);
        AssertUtil.isTrue(courseInfosTotalAmount.isSuccess(), courseInfosTotalAmount.getMessage());
        AssertUtil.isNotNull(courseInfosTotalAmount.getData(), "课程不存在");
        // 大VO
        CourseInfosTotalAmountVo courseInfosTotalAmountVo = JSONObject.parseObject(JSON.toJSONString(courseInfosTotalAmount.getData()), CourseInfosTotalAmountVo.class);
        // 小vo
        List<CourseInfosVo> courseInfos = courseInfosTotalAmountVo.getCourseInfos();
        //3.创建订单
        CourseOrder order = new CourseOrder();
        Date now = new Date();
        order.setCreateTime(now);
        order.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId)); // 生成订单编号
        order.setStatusOrder(CourseOrder.STATE_WAIT_PAY); // 等待支付
        order.setTotalAmount(courseInfosTotalAmountVo.getTotalAmount());
        order.setUserId(loginId);
        order.setPayType(coursePlaceOrderDto.getPayType());
       // insert(order);
        //4.创建订单明细，创建订单，保存数据库
        StringBuilder sb = new StringBuilder();
        sb.append("课程【");
        courseInfos.stream().forEach(courseInfo -> {
            Course course = courseInfo.getCourse();
            CourseMarket courseMarket = courseInfo.getCourseMarket();
            boolean isOnLine = course.getStatus() == Course.STATE_ON_LINE;
            AssertUtil.isTrue(isOnLine, "课程未上架");
            CourseOrderItem item = new CourseOrderItem();

            item.setCount(1);
            item.setAmount(courseMarket.getPrice());
            item.setCourseId(course.getId());
            item.setCreateTime(now);
            item.setCourseName(course.getName());
            item.setOrderNo(order.getOrderNo());
            item.setCoursePic(course.getPic());
           // item.setOrderId(order.getId());
          //  courseOrderItemService.insert(item);

            order.setTotalCount(order.getTotalCount() + 1);
            sb.append(course.getName()).append("--");
            order.getItems().add(item);
        });
        sb.append("】订单");
        order.setTitle(sb.toString());
       // updateById(order);
        //5.发送保存支付单的事务消息
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("loginId",loginId);
        hashMap.put("courseId",CourseIds);

        Order2PayParamDto order2PayParamDto = new Order2PayParamDto(
                order.getTotalAmount(),
                coursePlaceOrderDto.getPayType(),
                order.getOrderNo(),
                order.getUserId(),
                JSON.toJSONString(hashMap),
                order.getTitle()
        );
        // 此处order2PayParamDto不被识别，JSON.toJSONString(order2PayParamDto))转一下
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(order2PayParamDto)).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxOrder2PayGroup",//同监听器组名
                "order-topic:payorder-tags",
                message,
                order
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息的状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");
        // 两个状态 任意一个不成功就抛异常
        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            throw new GlobleException("下单失败！请重新下单","666");
        }

        //6.删除防重复token
        redisTemplate.delete(key);
        // 7.支付超时发送订单取消的延迟消息
        try {
            Message<String> delayMessage = MessageBuilder.withPayload(order.getOrderNo()).build();
            SendResult syncSend = rocketMQTemplate.syncSend(
                    "delay-order-topic:paytimeout-tags",
                    delayMessage,
                    3000, // 发送消息的时间每隔3秒
                    4  // 延时等级 30秒
            );
            SendStatus sendSendStatus = syncSend.getSendStatus();
            if(sendSendStatus != SendStatus.SEND_OK){
                //发送消息失败  可以返回兜底数据
                //1.重试3次
                //2.将消息体保存到数据库（提供管理界面，手动触发发布消息），发钉钉，短信，邮件通知运维介入处理
            }
        } catch (Exception e) {
            e.printStackTrace();
            //发送消息失败  可以返回兜底数据
            //1.重试3次
            //2.将消息体保存到数据库（提供管理界面，手动触发发布消息），发钉钉，短信，邮件通知运维介入处理
        }

        // 返回订单号
        return order.getOrderNo();


    }
    /* 
    * @Title: saveOrderAndItems
    * @Description: 保存订单和子订单信息
    * @Author: Mr.Jiang
    * @Version: 1.0
    * @Date:  2022/8/20 21:47  
    * @Parameters: [courseOrder]
    * @Return void
    */
    @Override
    public void saveOrderAndItems(CourseOrder courseOrder) {
        // 0.参数校验
        // 此处不需要再做参数校验，如果是空那么在事务监听器哪里就已经回滚了。
        // 1.业务校验
        // 1.1 根据订单号查是否存在订单了
        CourseOrder courseOrderTem = selectByOrderNo(courseOrder.getOrderNo());
        AssertUtil.isNull(courseOrderTem,"订单已经存在咯，不要重复下单");
        // 1.2若果没有就直接保存订单
        insert(courseOrder);
        for (CourseOrderItem item : courseOrder.getItems()) {
            item.setOrderId(courseOrder.getId());  // 设置主订单ID重要
            courseOrderItemService.insert(item);
        }

    }

    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper= new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }
/*
* @Title: upDateByNotify
* @Description: 处理支付结果，修改订单状态
* @Author: Mr.Jiang
* @Version: 1.0
* @Date:  2022/8/22 20:31
* @Parameters: [notifyDto]
* @Return void
*/
    @Override
    public void upDateByNotify(PayAliNotifyDto notifyDto) {
        CourseOrder courseOrder = selectByOrderNo(notifyDto.getOrderNo());
  if (courseOrder.getStatusOrder() == CourseOrder.STATE_WAIT_PAY){
      courseOrder.setStatusOrder(CourseOrder.STATE_SUCCESS);
      Date date = new Date();
      courseOrder.setUpdateTime(date);
      // 更新状态
      updateById(courseOrder);
  }else{
      //支付成功了，但是订单的状态不对或者还没有支付成功，就被手动取消了
      // 走人工
  }
    }
    /* 
    * @Title: cancelOrderByPayTimeOut
    * @Description: 支付超时取消
    * @Author: Mr.Jiang
    * @Version: 1.0
    * @Date:  2022/8/22 20:44  
    * @Parameters: [orderNo]
    * @Return void
    */
    @Override
    public void cancelOrderByPayTimeOut(String orderNo) {
        CourseOrder courseOrder = selectByOrderNo(orderNo);
        if(courseOrder == null){
            return; // 不管
        }
        //1.只有是待支付才能取消
        boolean isWaitPay = courseOrder.getStatusOrder() == CourseOrder.STATE_WAIT_PAY;
        if(!isWaitPay){
            return;
        }
        courseOrder.setStatusOrder(CourseOrder.STATE_CANCEL);
        courseOrder.setUpdateTime(new Date());
        updateById(courseOrder);
    }

}
